#!/usr/bin/env bash

source "$rvm_scripts_path/base"
source "$rvm_scripts_path/functions/build" # For gems with C extensions.

gemset_list_all()
{
  for rvm_ruby_string in $( __rvm_list_strings )
  do
    (__rvm_use ; gemset_list)
  done
  unset rvm_ruby_string
}

gemset_list_strings()
{
  \typeset gem_string
  for rvm_ruby_string in $( __rvm_list_strings )
  do
    for gem_string in "${rvm_gems_path:-${rvm_path}/gems}/${rvm_ruby_string}" "${rvm_gems_path:-${rvm_path}/gems}/${rvm_ruby_string}${rvm_gemset_separator:-@}"*
    do
      printf "%b" "${gem_string##*/}\n"
    done
  done
  unset rvm_ruby_string
}

gemset_update()
{

  if
    [[ -z "$rvm_ruby_strings" ]]
  then
    rvm_log "Running gem update for all rubies and gemsets."
  else
    rvm_log "Running gem update for the specified rubies."
  fi
  export rvm_ruby_strings="${rvm_ruby_strings:-all-gemsets}"
  export rvm_verbose_flag=1
  "$rvm_scripts_path/set" do gem update
  return $?
}

gemset_globalcache()
{
  \typeset gc_status globalcache_enabled directories directory_name \
    full_directory_path directory_name

  if [[ "$1" == "enabled" ]]
  then
    if __rvm_using_gemset_globalcache
    then
      gc_status="Enabled"
      globalcache_enabled=0
    else
      gc_status="Disabled"
      globalcache_enabled=1
    fi
    rvm_log "Gemset global cache is currently: $gc_status"
    return $globalcache_enabled
  elif [[ "$1" == "disable" ]]
  then
    rvm_log "Removing the global cache (note: this will empty the caches)"
    __rvm_read_lines directories <(
      __rvm_cd "${rvm_gems_path:-"$rvm_path/gems"}" ;
      __rvm_find . -maxdepth 1 -mindepth 1 -type d -print | cut -c 3-
    )

    for directory_name in "${directories[@]//.\/}"
    do
      current_cache_path="${rvm_gems_path:-"$rvm_path/gems"}/$directory_name/cache"
      if [[ -L "$current_cache_path" \
        && "$(__rvm_readlink "$current_cache_path")" == "$rvm_gems_cache_path" ]]
      then
        rvm_log "Reverting the gem cache for $directory_name to an empty directory."
        \command \rm -f "$current_cache_path" 2>/dev/null
        mkdir -p "$current_cache_path" 2>/dev/null
      fi
    done
    __rvm_db_ "$rvm_user_path/db" "use_gemset_globalcache" "delete"
  elif [[ "$1" == "enable" ]]
  then
    rvm_log "Enabling global cache for gems."
    mkdir -p "$rvm_gems_cache_path"
    __rvm_read_lines directories <(
      __rvm_cd "${rvm_gems_path:-"$rvm_path/gems"}" ;
      __rvm_find . -maxdepth 1 -mindepth 1 -type d -print | cut -c 3-
    )
    for directory_name in "${directories[@]//.\/}"
    do
      current_cache_path="${rvm_gems_path:-"$rvm_path/gems"}/$directory_name/cache"
      if [[ -d "$current_cache_path" && ! -L "$current_cache_path" ]]
      then
        rvm_log "Moving the gem cache for $directory_name to the global cache."
        \command \mv "$current_cache_path/"*.gem "$rvm_gems_cache_path/" 2>/dev/null
        case "${current_cache_path%\/}" in
          *cache)
            __rvm_rm_rf "$current_cache_path"
            ln -fs "$rvm_gems_cache_path" "$current_cache_path"
            ;;
        esac
      fi
    done
    __rvm_db_ "$rvm_user_path/db" "use_gemset_globalcache" "true"
  else
    printf "%b" "

  Usage:

    rvm gemset globalcache {enable,disable,enabled}

    Enable / Disable / Status the use of a global gem cachedir.

"
    return 1
  fi
}

gemset_name()
{
  echo "${rvm_ruby_gem_home##*${rvm_gemset_separator:-"@"}}"
  return $?
}

gemset_dir()
{
  echo "$rvm_ruby_gem_home"
  return $?
}

__gemset_list_single()
{
  \typeset gemset current_gemset ruby_name default_name
  gemset="$1"
  current_gemset="$2"
  gemset="${gemset##*/}"
  ruby_name="${gemset%%${rvm_gemset_separator:-@}*}"
  gemset="${gemset#${ruby_name}}"
  gemset="${gemset#${rvm_gemset_separator:-@}}"

  if [[ "${_second_param}" == "strings" ]]
  then default_name="default"
  else default_name="(default)"
  fi
  [[ -n "${gemset}"         ]] || gemset="$default_name"
  [[ -n "${current_gemset}" ]] || current_gemset="$default_name"
  if
    [[ "${gemset}" == "${current_gemset}" ]]
  then
    if [[ "${_second_param}" == "strings" ]]
    then echo "${gemset} #current"
    else echo "=> ${gemset}"
    fi
  else
    if [[ "${_second_param}" == "strings" ]]
    then echo "${gemset}"
    else echo "   ${gemset}"
    fi
  fi
}

gemset_list()
{
  [[ -d "${rvm_gems_path:-"$rvm_path/gems"}" ]] || {
    rvm_error "${rvm_gems_path:-"$rvm_path/gems"} does not exist!"
    return 1
  }
  [[ -n "${rvm_ruby_string:-""}" ]] || {
    rvm_error "\$rvm_ruby_string is not set!"
    return 1
  }

  \typeset current_gemset IFS
  current_gemset=$(__rvm_current_gemset)
  IFS=""

  [[ "${_second_param}" == "strings" ]] ||
    rvm_log "\ngemsets for $rvm_ruby_string (found in ${rvm_gems_path:-"$rvm_path/gems"}/$rvm_ruby_string)"

  for gemset in ${rvm_gems_path:-${rvm_path}/gems}/${rvm_ruby_string} ${rvm_gems_path:-${rvm_path}/gems}/${rvm_ruby_string}${rvm_gemset_separator:-@}*
  do
    __gemset_list_single "${gemset}" "${current_gemset}"
  done

  [[ "${_second_param}" == "strings" ]] || printf "%b" "\n"
}

gemset_delete_task()
{
  \typeset rvm_gemset_name ruby_at_gemset gemdir
  rvm_gemset_name=$1
  ruby_at_gemset=$2
  gemdir=$3

  case ${_system_type} in
    (BSD)
      # will not find broken links
      __rvm_find "${rvm_bin_path:=$rvm_path/bin}" -name \*${ruby_at_gemset} -exec rm -rf '{}' \;
      __rvm_find -L "${rvm_bin_path:=$rvm_path/bin}" -name \*${ruby_at_gemset} -exec rm -rf '{}' \;
      ;;
    (*)
      __rvm_find "${rvm_bin_path:=$rvm_path/bin}" \( -name \*${ruby_at_gemset} -or -lname \*${ruby_at_gemset}/\* \) -delete
      ;;
  esac
  for item in $( $rvm_scripts_path/alias search_by_target ${ruby_at_gemset} )
  do
    $rvm_scripts_path/alias delete ${item}
  done
  __rvm_rm_rf "$gemdir" &&
  __rvm_rm_rf "${rvm_wrappers_path:="$rvm_path/wrappers"}/${ruby_at_gemset}" &&
  __rvm_rm_rf "${rvm_environments_path:="$rvm_path/environments"}/${ruby_at_gemset}"
}

gemset_delete()
{
  [[ -n "${GEM_HOME:-}" ]] ||
  {
    rvm_error "A ruby must be selected in order to delete a gemset."
    return 1
  }
  if (( ${rvm_ruby_selected_flag:-0} == 0))
  then __rvm_ruby_string
  fi
  [[ -n "$rvm_gemset_name" ]] ||
  {
    rvm_error "A gemset name must be specified in order to delete a gemset."
    return 1
  }

  ruby_at_gemset="$rvm_ruby_string${rvm_gemset_separator:-"@"}$rvm_gemset_name"
  gemdir="${rvm_gems_path:-$rvm_path/gems}/${ruby_at_gemset}"

  [[ -e "$gemdir" ]] ||
  {
    rvm_warn "$gemdir did not previously exist. Ignoring."
    return 0
  }

  (( ${rvm_force_flag:-0} )) ||
  __rvm_ask_for "Are you SURE you wish to remove the entire gemset directory '$rvm_gemset_name' ($gemdir)?" yes ||
  {
    rvm_log "Not doing anything, phew... close call that one eh?"
    return 2
  }

  __rvm_log_command "gemset.delete" "Removing gemset $rvm_gemset_name" \
    gemset_delete_task "$rvm_gemset_name" "$ruby_at_gemset" "$gemdir"
}

gemset_empty()
{
  \typeset gemdir entry
  if
    [[ -n "${GEM_HOME}" ]]
  then
    if
      [[ ${rvm_force_flag:-0} -gt 0 ]] ||
      __rvm_ask_for "Are you SURE you wish to remove the installed gems for ${GEM_HOME}?" yes
    then
      gem_install gem-empty &&
      \command \gem empty &&
      rvm_log "Removed successfully." ||
      return $?
    else
      rvm_log "Not doing anything, phew... close call that one eh?"
      return 2
    fi
  fi
}

# Migrate gemsets from ruby X to ruby Y
gemset_copy()
{
  \typeset source_ruby destination_ruby source_path destination_path

  # Clear the current environment so that it does not influence this operation.
  unset rvm_gemset_name rvm_ruby_gem_home GEM_HOME GEM_PATH

  source_ruby="${1:-}"
  destination_ruby="${2:-}"
  shift 2
  if
    [[ -z "$destination_ruby" || -z "$source_ruby" ]]
  then
    rvm_help gemset copy
    return 1
  fi
  # Verify the destination gemset exists before attempting to use it.
  (
    rvm_ruby_string="$destination_ruby"
    export rvm_create_flag=1
    { __rvm_ruby_string && __rvm_gemset_select; } 2> /dev/null
  ) ||
  {
    rvm_error "Destination gemset '$destination_ruby' does not yet exist."
    return 1
  }

  # TODO: Account for more possibilities:
  #   rvm gemset copy 1.9.2 @gemsetb        # From 1.9.2 default to current ruby, 1.9.2 exists.
  #   rvm gemset copy @gemseta @gemsetb     # Current ruby, gemseta exists.
  #   rvm gemset copy gemseta gemsetb       # Currently Ruby, gemseta exists.
  #   rvm gemset copy gemseta 1.8.7@gemsetb # Currently Ruby@gemseta, current ruby@gemseta exists.

  source_path=$(
    __rvm_select "$source_ruby" > /dev/null 2>&1
    echo $rvm_ruby_gem_home
  )
  destination_path=$(
    __rvm_select "$destination_ruby" > /dev/null 2>&1
    echo $rvm_ruby_gem_home${rvm_expected_gemset_name:+@}${rvm_expected_gemset_name:-}
  )
  if
    [[ -z "$source_path" || ! -d "$source_path" ]]
  then
    rvm_error "Unable to expand '$source_ruby' or directory does not exist."
    return 1
  elif
    [[ -z "$destination_path" ]]
  then
    rvm_error "Unable to expand '$destination_ruby'."
    return 1
  fi

  if
    [[ -d "$source_path" ]]
  then
    rvm_log "Copying gemset from $source_ruby to $destination_ruby"
    for dir in "$source_path"/*
    do
      if
        [[ -d "$dir" ]]
      then
        __rvm_cp -Rf "$dir" "$destination_path/"
      elif
        [[ -L "$dir" ]]
      then
        __rvm_cp "$dir" "$destination_path/${dir##*/}"
      else
        rvm_debug "$dir not a file or link, not copying"
      fi
    done
    __rvm_log_command "gemset.wrappers"  "Generating gemset wrappers ${destination_path##*/}" gemset_reset_env "${destination_path##*/}" &&
    __rvm_gemset_pristine "$destination_ruby"
  else
    rvm_error "Gems directory does not exist for $source_path ($source_path)"
    return 1
  fi
}

# Migrate gemsets from ruby X to ruby Y
gemset_rename_()
{
  \command \mv "$source_path" "$destination_path" &&
  \command \rm "${rvm_wrappers_path:="$rvm_path/wrappers"}/${source_path##*/}" &&
  \command \rm "${rvm_environments_path:="$rvm_path/environments"}/${source_path##*/}"
}

# Migrate gemsets from ruby X to ruby Y
gemset_rename()
{
  \typeset source_name destination_name source_path destination_path

  source_name="${1:-}"
  destination_name="${2:-""}"
  shift 2
  if
    [[ -z "$destination_name" || -z "$source_name" ]]
  then
    rvm_error "Source and destination gemsets must be specified: 'rvm gemset rename X Y'"
    return 1
  fi
  source_path="$(
    rvm_use_flag=0 rvm_silence_logging=1 rvm_gemset_name=${source_name} __rvm_use "${rvm_ruby_string}@${source_name}" 1>&2 ;
    gem env gemdir
  )"
  if
    [[ -z "$source_path" || ! -d "$source_path" ]]
  then
    rvm_error "gemset '$source_name' does not exist."
    return 2
  fi
  destination_path=${source_path/%$source_name/$destination_name}
  if
    [[ -d "$source_path" ]]
  then
    if
      [[ ! -d "$destination_path" ]]
    then
      __rvm_log_command "gemset.rename" "Renaming gemset ${source_path##*/} => ${destination_path##*/}" gemset_rename_ &&
      __rvm_log_command "gemset.reset"  "Reseting gemset ${destination_path##*/}" gemset_reset_env "${destination_path##*/}" &&
      __rvm_gemset_pristine "${destination_path##*/}"
    else
      rvm_error "Gemset $destination_name already exists!"
      return 1
    fi
  else
    rvm_error "Gems directory does not exist for $source_path ($source_path)"
    return 1
  fi
}

gemset_unpack()
{
  \typeset gems name directory version  _platforms
  directory="${1:-vendor/gems}"
  if
    [[ -n "$rvm_ruby_gem_home" ]]
  then
    export GEM_HOME="$rvm_ruby_gem_home"
    export GEM_PATH="$rvm_ruby_gem_home:$rvm_ruby_global_gems_path"
  fi

  rvm_log "Unpacking current environments gemset to ${directory}\n"
  unset -f gem

  while read gem_name version _platforms
  do
    ( command gem unpack "$gem_name" -v"$version" --target "$directory" )&
  done <<<"$(
    GEM_PATH="$GEM_HOME" __rvm_list_gems
  )"
  wait
  rvm_log "Unpacking into ${directory} complete\n"
  return 0
}

gemset_export()
{
  \typeset rvm_file_name gem_name version versions _platforms
  rvm_file_name="${1:-}"
  if
    [[ -n "$rvm_ruby_gem_home" ]]
  then
    export GEM_HOME="$rvm_ruby_gem_home"
    export GEM_PATH="$rvm_ruby_gem_home:$rvm_ruby_global_gems_path"
  fi
  if
    [[ -n "$rvm_file_name" ]]
  then
    [[ "${rvm_file_name}" == *Gemfile ]] || rvm_file_name="${rvm_file_name%.gems}.gems"
  else
    if [[ -n "$rvm_gemset_name" ]]
    then rvm_file_name="$rvm_gemset_name.gems"
    else rvm_file_name="default.gems"
    fi
  fi
  rvm_log "Exporting current environments gemset to $rvm_file_name"
  if [[ -f "$rvm_file_name" ]]
  then \command \rm -f "$rvm_file_name"
  fi
  if
    [[ "${rvm_file_name}" == *Gemfile ]]
  then
    printf "%b" "source 'https://rubygems.org'

#ruby=${GEM_HOME##*/}

"
  else
    printf "%b" "# $rvm_file_name generated gem export file. \
Note that any env variable settings will be missing. \
Append these after using a ';' field separator

"
  fi > "$rvm_file_name"
  if
    (( ${rvm_latest_flag:-0} == 0 ))
  then
    while read gem_name version _platforms
    do
      if [[ "${rvm_file_name}" == *Gemfile ]]
      then echo "gem '$gem_name', '$version'"
      else echo "$gem_name -v$version"
      fi
    done <<<"$( GEM_PATH="$GEM_HOME" __rvm_list_gems )"
  else
    while read gem_name versions
    do
      if [[ "${rvm_file_name}" == *Gemfile ]]
      then echo "gem '$gem_name'"
      else echo "$gem_name"
      fi
    done <<<"$( GEM_PATH="$GEM_HOME" gem list )"
  fi >> "$rvm_file_name"
}

# Output the user's current gem directory.
gemset_info()
{
  if
    (( ${rvm_user_flag:-0} == 1 ))
  then
    (__rvm_use system ; gem env | __rvm_grep "\- $HOME" | __rvm_awk '{print $NF}')
  elif
    [[ ${rvm_system_flag:-0} == 1 ]]
  then
    (__rvm_use system ; gem env $action system)
  elif
    [[ -n "${rvm_ruby_string:-""}${rvm_gemset_name:+${rvm_gemset_separator:-"@"}}${rvm_gemset_name:-}" ]]
  then
    #TODO: why on ubuntu oneiric 32bit it's not enough to use gem env? why need to use again???
    (
      __rvm_use "${rvm_ruby_string:-""}${rvm_gemset_name:+${rvm_gemset_separator:-"@"}}${rvm_gemset_name:-}"
      gem env $action
    )
  else
    gem env $action
  fi
  return $?
}

gemset_prune()
{
  \typeset temporary_cache_path live_cache_path gemset_name version versions cached_gem_name cached_file_path

  temporary_cache_path="$GEM_HOME/temporary-cache"
  live_cache_path="$GEM_HOME/cache"

  mkdir -p "$temporary_cache_path"
  rvm_log "Moving active gems into temporary cache..."
  while
    read gem_name version _platforms
  do
    cached_gem_name="${gem_name}-${version}.gem"
    cached_file_path="${live_cache_path}/${cached_gem_name}"
    if
      [[ -f "$cached_file_path" ]]
    then
      \command \mv "$cached_file_path" "${temporary_cache_path}/${cached_gem_name}"
    fi
  done <<<"$( GEM_PATH="$GEM_HOME" __rvm_list_gems )"

  rvm_log "Removing live cache and restoring temporary cache..."
  # Switch the cache back.
  __rvm_rm_rf "$live_cache_path"
  \command \mv "$temporary_cache_path" "$live_cache_path"
  return 0
}

gem_search()
{
  \typeset gemspec gemspecs gem_name option environment_id ruby_string name gem_version
  gem_name="${1:-}"
  option="${2:-}"

  if [[ -z "${gem_name}" ]]
  then
    return 0
  fi

  true "${rvm_gems_path:="$rvm_path/gems"}"

  __rvm_read_lines gemspecs <(
    __rvm_find "${rvm_gems_path}" -mindepth 3 -iname "${gem_name}*.gemspec"  -type f
  )

  if [[ "${option}" != "strings" ]]
  then
    printf "%-40s %-20s %-20s\n" "environment_id" "name" "version"
    printf "%b" "================================================================================\n"
  fi

  for gemspec in "${gemspecs[@]}"
  do
    environment_id="${gemspec//${rvm_gems_path}\/}"
    environment_id="${environment_id//\/*}"
    ruby_string="${environment_id//@*}"
    gemset_name="${environment_id//${ruby_string}}"
    name=${gemspec//*\/}
    name=${name/%.gemspec}
    gem_version=${name}
    name=${name%%-+([0-9\.])}
    gem_version=${gem_version#${name}-}
    if [[ "$name" == "$gem_version" ]]
    then gem_version=""
    fi

    if [[ "${option}" != "strings" ]]
    then
      printf "%-40s %-20s %-20s\n" "${environment_id}" "${name}" "${gem_version}"
    else
      printf "%b" "${environment_id}\n"
    fi
  done
}

action="$1"
(( $# == 0 )) || shift
export _second_param="${1:-}"
export rvm_gemset_name="${1:-}"
rvm_sticky_flag=1

gemset_actions_with_gem=(
  gemdir gempath gemhome home path version export dump import
  load pristine copy install initial prune rename update unpack
)
if
  [[ " ${gemset_actions_with_gem[*]} " == *" $action "* ]] &&
  ! builtin command -v gem > /dev/null
then
  rvm_error "'gem' was not found, cannot perform gem actions (Do you have an RVM ruby selected?)"
  exit 1
fi

if [[ " $* " == *" --force "* ]]
then export rvm_force_flag=1
fi

if
  [[ -z "$rvm_ruby_string" && "${GEM_HOME:-""}" == "${rvm_path}"* ]]
then
  rvm_ruby_string="${GEM_HOME##*/}"
  rvm_ruby_string="${rvm_ruby_string%%@*}"
fi
if
  [[ -z "$rvm_gemset_name" && "${GEM_HOME:-""}" == "${rvm_path}"* ]]
then
  rvm_gemset_name="${GEM_HOME##*/}"
  rvm_gemset_name="${rvm_gemset_name#${rvm_gemset_name%%@*}}"
  rvm_gemset_name="${rvm_gemset_name#@}"
fi

rvm_ruby_gem_home="${rvm_ruby_gem_home:-$GEM_HOME}"
if
  [[ ! -d "$rvm_ruby_gem_home" ]] &&
  builtin command -v gem > /dev/null 2>&1
then
  rvm_ruby_gem_home="$(gem env home)"
fi

case "$action" in
  import|load)
    if
      [[ -z "${rvm_ruby_strings:-""}" ]]
    then
      gemset_import "Installing gems" "$1"
    else
      rubies=()
      __rvm_custom_separated_array rubies , "$rvm_ruby_strings"
      for rvm_ruby_string in "${rubies[@]}"
      do
        __rvm_with "$rvm_ruby_string" gemset_import "Installing gems" "$1"
      done
    fi
    ;;
  export|dump)
    gemset_export "$@"
    ;;
  empty)
    __rvm_use "${1:+@}${1:-${GEM_HOME##*/}}"
    gemset_$action "$@"
    ;;
  create|initial|list)
    __rvm_select
    rvm_gemset_name="${rvm_gemset_name:-${rvm_expected_gemset_name:-}}"
    gemset_$action "$@"
    ;;
  copy|delete|dir|globalcache|list_all|list_strings|pristine|prune|rename|update|unpack|reset_env)
    gemset_$action "$@"
    ;;
  name|string)
    gemset_name "$@"
    ;;
  strings)
    gemset_list "$@"
    ;;
  gemdir|gempath|gemhome|home|path|version)
    gemset_info "$@"
    ;;
  install|search)
    gem_$action "$@"
    ;;
  clear)
    rvm_error "'rvm gemset clear' has been deprecated. Please use 'rvm use @default' instead."
    ;;
  help)
    rvm_help gemset
    ;;
  *)
    rvm_error_help "Unknown subcommand '$action'" gemset
    exit 1
    ;;
esac
